1 module collie.codec.http.headers; 2 3 import collie.utils.string; 4 import kiss.container.Vector; 5 import core.stdc.string; 6 import std.string; 7 import std.array; 8 9 public import collie.codec.http.headers.httpcommonheaders; 10 public import collie.codec.http.headers.httpmethod; 11 12 alias HTTPHeaders = HttpHeaders; 13 14 struct HttpHeaders 15 { 16 enum kInitialVectorReserve = 32; 17 18 /** 19 * Remove all instances of the given header, returning true if anything was 20 * removed and false if this header didn't exist in our set. 21 */ 22 bool remove(string name){ 23 HTTPHeaderCode code = headersHash(name); 24 if(code != HTTPHeaderCode.OTHER) 25 return remove(code); 26 bool removed = false; 27 for(size_t i = 0; i < _headersNames.length; ++i){ 28 if(_codes[i] != HTTPHeaderCode.OTHER) continue; 29 if(isSameIngnoreLowUp(name,_headersNames[i])){ 30 _codes[i] = HTTPHeaderCode.NONE; 31 _headersNames[i] = null; 32 _headerValues[i] = null; 33 _deletedCount ++; 34 removed = true; 35 } 36 } 37 return removed; 38 } 39 40 bool remove(HTTPHeaderCode code){ 41 bool removed = false; 42 HTTPHeaderCode[] codes = _codes; 43 HTTPHeaderCode * ptr = codes.ptr; 44 const size_t len = codes.length; 45 while(true) 46 { 47 size_t tlen = len - (ptr - codes.ptr); 48 ptr = cast(HTTPHeaderCode *)memchr(ptr,code,tlen); 49 if(ptr is null) 50 break; 51 tlen = ptr - codes.ptr; 52 ptr ++; 53 _codes[tlen] = HTTPHeaderCode.NONE; 54 _headersNames[tlen] = null; 55 _headerValues[tlen] = null; 56 _deletedCount ++; 57 removed = true; 58 } 59 return removed; 60 } 61 62 void add(string name, string value) 63 in{ 64 assert(name.length > 0); 65 } 66 body{ 67 HTTPHeaderCode code = headersHash(name); 68 _codes ~= (code); 69 _headersNames ~= ((code == HTTPHeaderCode.OTHER) ? name : HTTPHeaderCodeName[code]); 70 _headerValues~= value; 71 72 } 73 74 void add(HTTPHeaderCode code, string value) 75 { 76 if(code == HTTPHeaderCode.OTHER || code > HTTPHeaderCode.SEC_WEBSOCKET_ACCEPT) 77 return; 78 _codes ~= code; 79 _headersNames ~= HTTPHeaderCodeName[code]; 80 _headerValues~= value; 81 } 82 83 void set(string name,string value) 84 { 85 remove(name); 86 add(name, value); 87 } 88 89 void set(HTTPHeaderCode code, string value) 90 { 91 remove(code); 92 add(code, value); 93 } 94 95 bool exists(string name) 96 { 97 HTTPHeaderCode code = headersHash(name); 98 if(code != HTTPHeaderCode.OTHER) 99 return exists(code); 100 for(size_t i = 0; i < _headersNames.length; ++i){ 101 if(_codes[i] != HTTPHeaderCode.OTHER) continue; 102 if(isSameIngnoreLowUp(name,_headersNames[i])){ 103 return true; 104 } 105 } 106 return false; 107 } 108 109 bool exists(HTTPHeaderCode code) 110 { 111 HTTPHeaderCode[] codes = _codes; 112 return memchr(codes.ptr,code,codes.length) != null; 113 } 114 115 void removeAll() 116 { 117 _codes = (HTTPHeaderCode[]).init; 118 _headersNames = (string[]).init; 119 _headerValues = (string[]).init; 120 _deletedCount = 0; 121 } 122 123 int opApply(scope int delegate(string name,string value) opeartions) 124 { 125 int result = 0; 126 for(size_t i = 0; i < _headersNames.length; ++i) 127 { 128 result = opeartions(_headersNames[i], _headerValues[i]); 129 if(result) 130 break; 131 } 132 return result; 133 } 134 135 int opApply(scope int delegate(HTTPHeaderCode code,string name,string value) opeartions) 136 { 137 int result = 0; 138 for(size_t i = 0; i < _headersNames.length; ++i) 139 { 140 result = opeartions(_codes[i],_headersNames[i], _headerValues[i]); 141 if(result) 142 break; 143 } 144 return result; 145 } 146 147 HTTPHeaders dub() 148 { 149 HTTPHeaders header; 150 copyTo(header); 151 return header; 152 } 153 154 void copyTo(ref HTTPHeaders header) 155 { 156 foreach(code,name,value; this) 157 { 158 if(code == HTTPHeaderCode.NONE) continue; 159 if(code == HTTPHeaderCode.OTHER) 160 header.add(name,value); 161 else 162 header.add(code,value); 163 } 164 } 165 /** 166 * Get the total number of headers. 167 */ 168 size_t size() const{ 169 return _codes.length - _deletedCount; 170 } 171 /** 172 * combine all the value for this header into a string 173 */ 174 string combine(string separator = ", ") 175 { 176 Appender!string data = appender!string(); 177 bool frist = true; 178 foreach(code,name,value; this) 179 { 180 if(code == HTTPHeaderCode.NONE) continue; 181 if(frist) { 182 data.put(value); 183 frist = false; 184 } else { 185 data.put(separator); 186 data.put(value); 187 } 188 } 189 return data.data; 190 } 191 192 size_t getNumberOfValues(string name) 193 { 194 HTTPHeaderCode code = headersHash(name); 195 if(code != HTTPHeaderCode.OTHER) 196 return remove(code); 197 size_t index = 0; 198 for(size_t i = 0; i < _headersNames.length; ++i){ 199 if(_codes[i] != HTTPHeaderCode.OTHER) continue; 200 if(isSameIngnoreLowUp(name,_headersNames[i])){ 201 ++index; 202 } 203 } 204 return index; 205 } 206 207 size_t getNumberOfValues(HTTPHeaderCode code) 208 { 209 size_t index = 0; 210 HTTPHeaderCode[] codes = _codes; 211 HTTPHeaderCode * ptr = codes.ptr; 212 const size_t len = codes.length; 213 while(true) 214 { 215 size_t tlen = len - (ptr - codes.ptr); 216 ptr = cast(HTTPHeaderCode *)memchr(ptr,code,tlen); 217 if(ptr is null) 218 break; 219 ptr ++; 220 ++ index; 221 } 222 return index; 223 } 224 225 string getSingleOrEmpty(string name) { 226 HTTPHeaderCode code = headersHash(name); 227 if(code != HTTPHeaderCode.OTHER) 228 return getSingleOrEmpty(code); 229 for(size_t i = 0; i < _headersNames.length; ++i){ 230 if(_codes[i] != HTTPHeaderCode.OTHER) continue; 231 if(isSameIngnoreLowUp(name,_headersNames[i])){ 232 return _headerValues[i]; 233 } 234 } 235 return string.init; 236 } 237 238 string getSingleOrEmpty(HTTPHeaderCode code) { 239 HTTPHeaderCode[] codes = _codes; 240 HTTPHeaderCode * ptr = cast(HTTPHeaderCode *)memchr(codes.ptr,code,codes.length); 241 if(ptr !is null){ 242 size_t index = ptr - codes.ptr; 243 return _headerValues[index]; 244 } 245 return string.init; 246 } 247 248 string[] getValuesByKey(string name) 249 { 250 HTTPHeaderCode code = headersHash(name); 251 if(code != HTTPHeaderCode.OTHER) 252 { 253 remove(code); 254 return null; 255 } 256 257 string[] r = null; 258 for(size_t i = 0; i < _headersNames.length; ++i){ 259 if(_codes[i] != HTTPHeaderCode.OTHER) continue; 260 261 if(isSameIngnoreLowUp(name,_headersNames[i])){ 262 r ~= _headerValues[i]; 263 } 264 } 265 return r; 266 } 267 268 /** 269 * Process the ordered list of values for the given header name: 270 * for each value, the function/functor/lambda-expression given as the second 271 * parameter will be executed. It should take one const string & parameter 272 * and return bool (false to keep processing, true to stop it). Example use: 273 * hdrs.forEachValueOfHeader("someheader", [&] (const string& val) { 274 * std::cout << val; 275 * return false; 276 * }); 277 * This method returns true if processing was stopped (by func returning 278 * true), and false otherwise. 279 */ 280 alias LAMBDA = bool delegate(string value); 281 bool forEachValueOfHeader(string name,scope LAMBDA func) 282 { 283 HTTPHeaderCode code = headersHash(name); 284 if(code != HTTPHeaderCode.OTHER) 285 return forEachValueOfHeader(code,func); 286 size_t index = 0; 287 for(size_t i = 0; i < _headersNames.length; ++i){ 288 if(_codes[i] != HTTPHeaderCode.OTHER) continue; 289 if(isSameIngnoreLowUp(name,_headersNames[i])){ 290 if(func(_headerValues[i])) 291 return true; 292 } 293 } 294 return false; 295 } 296 297 bool forEachValueOfHeader(HTTPHeaderCode code,scope LAMBDA func) 298 { 299 size_t index = 0; 300 HTTPHeaderCode[] codes = _codes; 301 HTTPHeaderCode * ptr = codes.ptr; 302 const size_t len = codes.length; 303 while(true) 304 { 305 size_t tlen = len - (ptr - codes.ptr); 306 ptr = cast(HTTPHeaderCode *)memchr(ptr,code,tlen); 307 if(ptr is null) 308 break; 309 tlen = ptr - codes.ptr; 310 ptr ++; 311 if(func(_headerValues[tlen])) 312 return true; 313 } 314 return false; 315 } 316 private: 317 HTTPHeaderCode[] _codes ;// = Vector!(HTTPHeaderCode)(2); 318 string[] _headersNames ; 319 string[] _headerValues ; 320 size_t _deletedCount = 0; 321 }